<!--  -->
<template>
    <div class="ai-main">
        <div class="ai-container"></div>

        <div style="position:absolute;right:0;top:0;">
            <a-button @click="run">运行</a-button>
            <a-button @click="test">测试</a-button>
        </div>

        <a-menu v-show="showContextMenu && contextMenu.length" class="context-menu" style="width: 170px" mode="vertical">
            <template v-for="(menu, index) in contextMenu">
                <a-menu-item v-if="menu.type === 'menu'" :key="index" :disabled="menu.runDisable && runningState" @click="menuItemCallback(menu)">
                    <svg-icon :icon-class="menu.icon"></svg-icon>
                    {{ menu.title }}
                </a-menu-item>
                <a-menu-divider v-else-if="menu.type === 'divider'" :key="index" />
                <a-sub-menu v-else-if="menu.type === 'subMenu' && Array.isArray(menu.children)" :key="index" :disabled="menu.runDisable && runningState">
                    <span slot="title">
                        <svg-icon :icon-class="menu.icon"></svg-icon>
                        <span>{{ menu.title }}</span>
                    </span>
                    <a-menu-item v-for="(subMenu, subIndex) in menu.children" :key="subIndex" @click="menuItemCallback(subMenu)">
                        <svg-icon :icon-class="subMenu.icon"></svg-icon>
                        {{ subMenu.title }}
                    </a-menu-item>
                </a-sub-menu>
            </template>
        </a-menu>
    </div>
</template>

<script>
import G6 from '@antv/g6';
import registerElement from '@/components/flowGraph/element';
import registerBehavior from '@/components/flowGraph/behavior';
import _ from 'lodash';
import { flowData, componentList } from './datas/data';

import moment from 'moment';

const contextMenu = {
    rename: {
        type: 'menu',
        title: '重命名',
        icon: 'da_rename',
        callback: 'rename',
        runDisable: true,
    },
    delete: {
        type: 'menu',
        title: '删除',
        icon: 'delete',
        callback: 'delModel',
        runDisable: true,
    },
    executeFrom: {
        type: 'menu',
        title: '从此处开始执行',
        icon: 'da_run_from',
        callback: 'executeFrom',
        runDisable: true,
    },
    excuteHere: {
        type: 'menu',
        title: '执行到此处',
        icon: 'da_run_to',
        callback: 'excuteHere',
        runDisable: true,
    },
    excute: {
        type: 'menu',
        title: '执行该节点',
        icon: 'da_run_person',
        callback: 'excute',
        runDisable: true,
    },
    totalRun: {
        type: 'menu',
        title: '全部执行',
        icon: 'da_run_nomal',
        callback: 'run',
        runDisable: true,
        //是否小批量运行
        isTestRun: false,
    },
    testTotalRun: {
        type: 'menu',
        title: '全部执行',
        icon: 'da_run_nomal',
        callback: 'run',
        //是否小批量运行
        isTestRun: true,
    },
    testRun: {
        type: 'subMenu',
        title: '小数据量试运行',
        icon: 'da_run_nomal',
        runDisable: true,
    },
    viewData: {
        type: 'menu',
        title: '查看数据',
        icon: 'kejian',
        callback: 'viewData',
        outPoint: 0,
    },
    viewSubData: {
        type: 'subMenu',
        title: '查看数据',
        icon: 'kejian',
    },
    viewReport: {
        type: 'menu',
        title: '查看评估报告',
        icon: 'kejian',
        callback: 'viewReport',
    },
    viewLog: {
        type: 'menu',
        title: '查看日志',
        icon: 'da_data_view',
        callback: 'viewLog',
    },
    divider: {
        type: 'divider',
    },
};

export default {
    inheritAttrs: false,
    props: {},
    data() {
        return {
            flowData: _.cloneDeep(flowData),
            componentList,
            contextMenu: [],
            showContextMenu: false,
            runningState: false,
        };
    },
    components: {},
    computed: {},
    watch: {},
    created() {},
    mounted() {
        this.initGraph();

        document.addEventListener('click', this.hiddenContextMenu);
        document.addEventListener('contextmenu', this.hiddenContextMenu, true);
    },
    beforeDestroy() {
        document.removeEventListener('click', this.hiddenContextMenu);
        document.removeEventListener('contextmenu', this.hiddenContextMenu);
    },
    methods: {
        test() {
            const item = this.flowGraph.get('selectedItems');
            console.error(item, 'item');

            const saveData = this.flowGraph.save();
            console.log(saveData, '模型数据');
        },
        run() {
            // const nodeArray = this.getRunningData();
            // this.runningState = true;
            // this.backupFormData(nodeArray);
            // this.running(nodeArray, this.checkState).then(() => {
            //     this.runningState = false;
            // });
            this.flowGraph.getEdges().forEach((edge) => {
                this.flowGraph.setItemState(edge, 'running', true);
            });

            this.flowGraph.getNodes().forEach((node) => {
                this.flowGraph.updateItem(node, { type: 'running' });
            });
            setTimeout(() => {
                this.flowGraph.getEdges().forEach((edge) => {
                    this.flowGraph.setItemState(edge, 'running', false);
                });

                this.flowGraph.getNodes().forEach((node) => {
                    this.flowGraph.updateItem(node, { type: 'success' });
                });
            }, 2000);
        },
        initGraph() {
            if (!this.flowGraph) {
                const grid = new G6.Grid();
                const designerBox = this.$el.querySelector('.ai-container');
                const designerRect = designerBox?.getBoundingClientRect();
                this.flowGraph = new G6.Graph({
                    plugins: [grid],
                    container: designerBox,
                    width: designerRect?.width ?? 800,
                    height: designerRect?.height ?? 500,
                    defaultEdge: {
                        type: 'flow-cubic-round',
                    },
                    defaultNode: {
                        type: 'normal',
                    },
                    modes: {
                        default: ['drag-canvas', 'zoom-canvas'],
                        edit: [
                            'drag-canvas',
                            'zoom-canvas',
                            'dragNode',
                            'dragEdge',
                            'hoverElementActived',
                            'hoverAnchorActived',
                            'elementSelected',
                            {
                                type: 'nodeTooltip',
                                formatText: this.nodeFormatText,
                            },
                        ],
                        // preview: [
                        //     'drag-canvas',
                        //     'zoom-canvas',
                        //     'dragNode',
                        //     'hoverElementActived',
                        //     'hoverAnchorActived',
                        //     'elementSelected',
                        //     {
                        //         type: 'nodeTooltip',
                        //         formatText: this.nodeFormatText,
                        //     },
                        // ],
                    },
                });
                // 绑在实例上
                this.flowGraph.onContextmenu = this.onContextmenu;
                this.flowGraph.elementClick = this.elementClick;
                this.flowGraph.linkAnchorRule = this.linkAnchorRule;
                registerElement(G6);
                registerBehavior(G6);

                // todo:  锚点问题尚未解决
                this.flowGraph.setMode('edit');

                this.flowGraph.node((node) => {
                    //  mark:
                    const mappingSD = this.componentList?.find((sd) => {
                        return sd.value === node.busData.name;
                    });
                    let additionNode = {
                        contextMenus: mappingSD?.menus,
                        sourceName: mappingSD?.label,
                        description: mappingSD?.description,
                    };
                    additionNode.anchorConfig = {
                        top: mappingSD.inputs,
                        bottom: mappingSD.outputs,
                    };
                    // 保存的模型有配置好的 formData （特殊处理）
                    if (mappingSD?.formData) additionNode.formData = mappingSD.formData;

                    //保存表单数据的备份，用于在执行成功的节点在表单变化后，在节点上进行标记
                    if (!node.mappingFormData) {
                        additionNode.mappingFormData = _.cloneDeep(node.formData);
                    }
                    try {
                        const img = require(`@/components/flowGraph/icon/ia_${mappingSD?.iconUseSupType ? node.busData.subType : node.busData.type}.svg`);
                        if (img) additionNode.logoIcon = { img };
                    } catch (e) {
                        console.error(e);
                    }
                    return Object.assign({}, node, additionNode);
                });
                console.log('g6 构建好了');
                this.renderGraph();
            }
        },
        /**
         * 初始化、兼容数据：
         */
        compatibleData(flowData) {
            // g6升级 兼容之前的数据格式
            flowData?.nodes?.forEach((node) => {
                node.type = node.type ?? node.shape;
            });
            flowData?.edges?.forEach((edge) => {
                edge.type = edge.type ?? edge.shape;

                //数据兼容，锚点索引，后台输出逻辑索引从0开始，前端输出逻辑索引接输入索引延续
                const sourceNode = flowData?.nodes?.find((node) => node.id === edge.source);
                if (sourceNode) {
                    const mappingSD = this.componentList?.find((sd) => {
                        return sd.value === sourceNode.busData.name;
                    });

                    let sourceOutputLength = 0;
                    if (mappingSD) {
                        // inputs outputs
                        sourceOutputLength = mappingSD?.inputs?.length ?? 0;
                    }
                    edge.sourceAnchor = edge.sourceAnchor + sourceOutputLength;
                }
            });
        },
        renderGraph() {
            this.compatibleData(this.flowData);
            this.flowGraph.changeData(this.flowData);
        },
        onContextmenu(options) {
            const { coord, model } = options;
            //不响应锚点
            if (!model.id) return;
            this.contextMenu.length = 0;
            this.contextModel = model;

            const busData = model.busData;
            if (busData) {
                let testRunMenu = _.cloneDeep(contextMenu.testRun);
                let _executeFrom = _.cloneDeep(contextMenu.executeFrom);
                _executeFrom.isTestRun = true;
                testRunMenu.children = [contextMenu.testTotalRun, _executeFrom];
                if (
                    (busData.type === 'metadata' && busData.subType === 'datasource') ||
                    (busData.type === 'metadata' && busData.subType === 'datatable') ||
                    busData.type === 'saved_model'
                ) {
                    // 根据权限判断右键菜单
                    let contextMenuArr = [];
                    contextMenuArr.push(contextMenu.rename);
                    contextMenuArr.push(contextMenu.delete);
                    {
                        if (contextMenuArr.length) contextMenuArr.push(contextMenu.divider);
                        contextMenuArr = contextMenuArr.concat([contextMenu.totalRun, contextMenu.executeFrom, testRunMenu]);
                    }
                    if (contextMenuArr.length) contextMenuArr.push(contextMenu.divider);
                    contextMenuArr = contextMenuArr.concat([contextMenu.viewData, contextMenu.viewLog]);
                    this.contextMenu = contextMenuArr;
                } else {
                    let subSize;
                    if (model.anchorConfig?.bottom) {
                        let outputLength;
                        if (typeof model.anchorConfig.bottom === 'number') {
                            outputLength = model.anchorConfig.bottom;
                        } else if (Array.isArray(model.anchorConfig.bottom)) {
                            outputLength = model.anchorConfig.bottom.length;
                        }
                        if (outputLength) {
                            subSize = outputLength;
                        } else {
                            subSize = 0;
                        }
                    }

                    let viewDataMenu;
                    if (subSize > 1) {
                        viewDataMenu = _.cloneDeep(contextMenu.viewSubData);
                        viewDataMenu.children = new Array(subSize).fill(0).map((_, index) => {
                            return {
                                type: 'menu',
                                title: '查看输出桩' + (index + 1),
                                icon: 'kejian',
                                callback: 'viewData',
                                outPoint: index,
                            };
                        });
                    } else {
                        viewDataMenu = contextMenu.viewData;
                    }

                    let appendChildren = [_.cloneDeep(contextMenu.excuteHere), _.cloneDeep(contextMenu.excute)];
                    appendChildren.forEach((c) => (c.isTestRun = true));
                    testRunMenu.children.push(...appendChildren);
                    // 根据权限判断右键菜单
                    let contextMenuArr = [];
                    contextMenuArr.push(contextMenu.rename);
                    contextMenuArr.push(contextMenu.delete);
                    {
                        if (contextMenuArr.length) contextMenuArr.push(contextMenu.divider);
                        contextMenuArr = contextMenuArr.concat([
                            contextMenu.totalRun,
                            contextMenu.executeFrom,
                            contextMenu.excuteHere,
                            contextMenu.excute,
                            testRunMenu,
                        ]);
                    }
                    if (contextMenuArr.length) contextMenuArr.push(contextMenu.divider);
                    contextMenuArr = contextMenuArr.concat([viewDataMenu]);
                    this.contextMenu = contextMenuArr;
                    //
                    if (Array.isArray(model.contextMenus)) {
                        let permModel;
                        permModel = model.contextMenus;
                        const extendMenus = permModel.map((menu) => {
                            if (Array.isArray(menu.childs)) {
                                return {
                                    type: 'subMenu',
                                    title: menu.name,
                                    icon: 'kejian',
                                    children: menu.childs.map((subMenu) => {
                                        return {
                                            type: 'menu',
                                            title: subMenu.name,
                                            value: model.busData.name,
                                            icon: 'kejian',
                                            callback: subMenu.key + 'View',
                                        };
                                    }),
                                };
                            } else {
                                return {
                                    type: 'menu',
                                    title: menu.name,
                                    value: model.busData.name,
                                    icon: 'kejian',
                                    callback: menu.key + 'View',
                                };
                            }
                        });
                        this.contextMenu.push(...extendMenus);
                    }

                    this.contextMenu.push(contextMenu.viewLog);
                }
            } else {
                //边
                this.contextMenu = [contextMenu.delete];
            }

            this.$nextTick(() => {
                const designerRect = this.$el.querySelector('.ai-container')?.getBoundingClientRect();
                const contextMenuDom = this.$el.querySelector('.context-menu');
                const menuRect = contextMenuDom.getBoundingClientRect();
                const parentPadding = 10;

                let top = coord.y + parentPadding;
                let left = coord.x + parentPadding;
                if (coord.y + menuRect.height > designerRect.height) {
                    top = coord.y - menuRect.height - 5;
                }
                if (coord.x + menuRect.width > designerRect.width) {
                    left = coord.x - menuRect.width - 5;
                }
                contextMenuDom.style.left = left + 'px';
                contextMenuDom.style.top = top + 'px';
            });
            this.showContextMenu = true;
        },
        menuItemCallback(menu) {
            console.error(menu, 'menuItemCallback 菜单');
            if (menu.callback) {
                this[menu.callback](menu);
            }
        },
        hiddenContextMenu() {
            this.showContextMenu = false;
        },
        //
        nodeFormatText(model) {
            let state = {
                success: '成功',
                fail: '失败',
                running: '运行中',
                normal: '就绪',
            };

            const busData = model.busData;
            let algorithmName;
            if (busData.type === 'metadata' && busData.subType === 'datasource') {
                algorithmName = `读取数据源表(${model.sourceName})`;
            } else if (busData.type === 'export') {
                algorithmName = '导出到数据库';
            } else {
                algorithmName = busData.name;
            }
            let timeText = null;
            if (['success', 'fail'].includes(model.type) && model.additional && model.additional.startTime && model.additional.endTime) {
                timeText = `
                    <tr>
                        <td>开始时间：</td>
                        <td>${moment(model.additional.startTime).format('YYYY-MM-DD HH:mm:ss')}</td>
                    </tr>
                    <tr>
                        <td>结束时间：</td>
                        <td>${moment(model.additional.endTime).format('YYYY-MM-DD HH:mm:ss')}</td>
                    </tr>
                `;
            }
            let retText = `
                <table>
                    <tbody>
                        <tr>
                            <td>节点名称：</td>
                            <td>${model.label}</td>
                        </tr>
                        <tr>
                            <td>算法名称：</td>
                            <td>${algorithmName}</td>
                        </tr>
            `;
            if (timeText) {
                retText += timeText;
            }
            retText += ` <tr>
                            <td>运行状态：</td>
                            <td>${state[model.type]}</td>
                        </tr>
                    </tbody>
                </table>
            `;
            return retText;
        },
        /**
         * 获取所有节点的运行数据结构
         * @return  isSource: 是否是图的起点; inNodeIds: 该节点的起始节点 ids; item; state: 节点状态
         */
        getRunningData() {
            let nodeArray = [];
            const nodes = this.flowGraph.getNodes();
            // 获取所有没有 入边 的节点
            const sourceNodes = nodes.filter((node) => {
                const inEdges = node.getInEdges();
                return !inEdges || !inEdges.length;
            });
            nodes.forEach((node) => {
                let pushFeature = {
                    item: node,
                    state: 0,
                };
                const inEdges = node.getInEdges();
                if (Array.isArray(inEdges) && inEdges.length > 0) {
                    pushFeature.isSource = false;
                    pushFeature.inNodeIds = [];
                    inEdges.forEach((edge) => {
                        const sNode = edge.getSource();
                        sourceNodes.push(sNode);
                        pushFeature.inNodeIds.push(sNode._cfg.id);
                    });
                } else {
                    pushFeature.isSource = true;
                }
                nodeArray.push(pushFeature);
            });
            return nodeArray;
        },
        /**
         * 依据运行数据结构运行包含的所有节点
         * @param {*} runningData
         * @param {*} runningHandle
         */
        async running(runningData, runningHandle) {
            if (!Array.isArray(runningData)) return;

            //循环找关联的源节点已经执行完毕，产生了结果的节点
            let nextRuningData;
            //保存之前的查询结果，避免重复请求, 这里要拿一下第一次的数据，看是否是运行中来判断每一个节点信息
            const res = await new Promise((resolve) => {
                setTimeout(() => {
                    resolve({
                        runState: 1,
                    });
                }, 2000);
            });
            const taskResult = res;

            let resultCache = {
                stateResult: taskResult,
                // stateResult: null
            };
            do {
                if (nextRuningData === undefined) {
                    nextRuningData = runningData.filter((feature) => feature.isSource);
                } else {
                    nextRuningData = runningData.filter((feature) => {
                        return (
                            feature.state === 0 &&
                            Array.isArray(feature.inNodeIds) &&
                            feature.inNodeIds
                                .map((nodeId) => {
                                    return runningData.find((_feature) => _feature.item._cfg.id === nodeId);
                                })
                                .filter((_feature) => !!_feature) //此处需要排除源节点不在运行节点列表中的情况，这种情况执行是否成功依赖服务端返回的结果
                                //.every(_feature => _feature.state !== 0)
                                .every((_feature) => _feature.state === 1)
                        );
                    });
                }
                if (nextRuningData.length) {
                    //为了避免两个节点异步执行，同步完成的问题，不能使用Promise.all
                    await this._fragmentRun(nextRuningData, runningHandle, resultCache);
                }
            } while (nextRuningData.length);

            // 拿全部节点的运行数据去保存数据集
            const nodeArray = this.getRunningData();
            await this.saveDataset(nodeArray);
            if (this.taskResult?.id) {
                this.pushLog({
                    type: 'ai',
                    prefix: '聚类分析',
                    label: this.editAi.name,
                    content: '全部运行完成。',
                });
            }
        },
        async _fragmentRun(runningData, runningHandle, resultCache) {
            runningData.forEach((feature) => {
                const inEdges = feature.item.getInEdges();
                if (Array.isArray(inEdges) && inEdges.length > 0) {
                    inEdges.forEach((edge) => {
                        this.flowGraph.setItemState(edge, 'running', true);
                    });
                }
                // const currentModel = feature.item.getModel();
                // if (resultCache?.stateResult?.runState === 1) {
                //     this.pushLog({
                //         prefix: '节点',
                //         label: currentModel.label,
                //         content: '开始运行'
                //     });
                // }
                this.flowGraph.updateItem(feature.item, { type: 'running' });
            });
            // console.log(runningData, 'runningData');
            // 运行中
            runningData.forEach((feature) => {
                const currentModel = feature.item.getModel();
                //  不能根据后台返回的 运行状态，有一点点时间差，立即训练成功不一定返回了运行中的状态。
                // if (resultCache?.stateResult?.runState === 1) {
                const nodeTask = resultCache?.stateResult?.tasks?.find((i) => i.name === currentModel?.id);
                if (nodeTask) {
                    this.pushLog({
                        prefix: '节点',
                        label: currentModel.label,
                        startTime: nodeTask?.startTime ? this.getTime(nodeTask.startTime) : this.getTime(),
                        endTime: nodeTask?.endTime ? this.getTime(nodeTask.endTime) : this.getTime(),
                        runState: 1,
                        isRunning: true,
                        content: '运行中',
                    });
                }

                // }
            });

            //runState,0-未运行，1-运行成功，2-准备就绪，-1-运行失败
            const stateShape = {
                '0': 'normal',
                '1': 'success',
                '-1': 'fail',
                '2': 'normal',
            };

            //按顺序执行，缓存前一次的请求数据，能尽量减少请求数量（回调中有保证何时重新请求的逻辑），保证执行效率
            let feature;
            for (let i = 0, iSize = runningData.length; i < iSize; i++) {
                feature = runningData[i];
                const currentModel = feature.item.getModel();

                let result = await runningHandle(feature.item, resultCache.stateResult);

                const inEdges = feature.item.getInEdges();
                if (Array.isArray(inEdges) && inEdges.length > 0) {
                    inEdges.forEach((edge) => {
                        this.flowGraph.setItemState(edge, 'running', false);
                    });
                }
                feature.state = result.runState;
                this.flowGraph.updateItem(feature.item, { type: stateShape[result.runState] });
                resultCache.stateResult = result.runResultData;
                console.log(resultCache, 'resultCache');
                console.log(result, 'result');
                if (result?.runResultData) {
                    const nodeTask = result?.runResultData?.tasks?.find((i) => i.name === feature.item._cfg.model?.id);
                    this.log.forEach((i) => (i.isRunning = false));
                    this.pushLog({
                        label: currentModel.label,
                        startTime: this.getTime(nodeTask.startTime),
                        endTime: this.getTime(nodeTask.endTime),
                        runState: nodeTask.runState,
                        isDone: nodeTask.runState == 2 || nodeTask.runState == 3,
                        content: nodeTask.runState === 2 ? '运行完成' : nodeTask.runState === 3 ? '运行失败' : '运行完成',
                        isRunning: false,
                        model: nodeTask.runState === 3 ? currentModel : null,
                    });
                }
            }
        },
        async checkState(nodeItem, preTaskResult) {
            this.flowGraph.setItemState(nodeItem, 'warn', false);
            let taskResult;
            if (preTaskResult) {
                taskResult = preTaskResult;
            } else {
                // const res = await api.runStateIndeptAnalysis(this.editAi.id);
                // taskResult = this.taskResult = res.data.rst;
            }

            let retResult = {
                runResultData: taskResult,
                //执行的结果状态
                runState: 0,
            };

            if (taskResult && Array.isArray(taskResult.tasks)) {
                const tasks = taskResult.tasks;
                const currentTask = tasks.find((t) => nodeItem.get('id') === t.name);
                let model = nodeItem.get('model');
                model.additional = model.additional || {};
                if (currentTask) {
                    //运行状态：3、失败；2、成功；1、运行中；0、准备就绪
                    //成功和失败后将这些信息挂在到节点中去
                    if (currentTask.runState === 2) {
                        retResult.runState = 1;
                        model.additional.startTime = currentTask.startTime;
                        model.additional.endTime = currentTask.endTime;
                    } else if (currentTask.runState === 3) {
                        retResult.runState = -1;
                        model.additional.startTime = currentTask.startTime;
                        model.additional.endTime = currentTask.endTime;
                    } else {
                        //进入该分支说明节点执行未出结果，间隔5秒钟后再查询一遍
                        return new Promise((resolve) => {
                            this.timer.checkState = setTimeout(() => {
                                resolve(this.checkState(nodeItem));
                            }, 5000);
                        });
                    }
                } else {
                    //找不到任务，直接判定失败
                    console.error('找不到任务节点，节点任务执行就绪【前端判定】');
                    retResult.runState = 2;
                }
            } else {
                //状态查询接口返回数据格式有误，直接判定失败
                console.error('状态查询接口返回数据格式有误，节点任务未执行【前端判定】');
                retResult.runState = 2;
            }
            // this.retResult = retResult;

            return retResult;
        },
        // 备份表单
        backupFormData(features) {
            features.forEach((f) => {
                //执行成功的节点需要将节点的当前表单进行备份
                let model = f.item.get('model');
                model.mappingFormData = _.cloneDeep(model.formData);
            });
            this.autoSave();
        },
        autoSave: _.debounce(function() {
            this.save(true);
        }, 300),
        save() {
            const graphData = _.cloneDeep(this.flowGraph.save());
            graphData.edges.forEach((edge) => {
                //数据兼容，锚点索引，后台输出逻辑索引从0开始，前端输出逻辑索引接输入索引延续
                const nodes = graphData.nodes;
                const sourceNode = nodes.find((node) => node.id === edge.source);
                if (sourceNode) {
                    let sourceOutputLength = 0;
                    if (sourceNode.anchorConfig) {
                        if (sourceNode.anchorConfig.top) {
                            if (typeof sourceNode.anchorConfig.top === 'number') {
                                sourceOutputLength = sourceNode.anchorConfig.top;
                            } else if (Array.isArray(sourceNode.anchorConfig.top)) {
                                sourceOutputLength = sourceNode.anchorConfig.top.length;
                            }
                        }
                    }
                    edge.sourceAnchor = edge.sourceAnchor - sourceOutputLength;
                }
            });
            //去除冗余信息
            graphData.nodes.forEach((node) => {
                delete node.additional;
                delete node.anchorConfig;
                delete node.logoIcon;
                delete node.description;
                delete node.contextMenus;

                // [业务] 对数据格式做下兼容性处理
                if (node.formData) {
                    // delete node.mappingFormData;
                    for (const key in node.formData) {
                        const strValue = JSON.stringify(node.formData[key]);
                        //0==[],[]!==[]
                        if (strValue === '[]' || strValue === '""') {
                            node.formData[key] = null;
                        }
                    }
                }
            });
            console.error('保存的数据', graphData);
        },
        /**
         * 工具
         */
        linkAnchorRule(sourceNode, sourceAnchorIndex, targetNode) {
            const sourceModel = sourceNode.get('model');
            const targetModel = targetNode.get('model');

            //节点不能连接自己
            if (sourceModel.id === targetModel.id) {
                return false;
            }

            //输入锚点不能连接到任何锚点
            let sourceInputLength = 0;
            if (sourceModel.anchorConfig) {
                if (sourceModel.anchorConfig.top) {
                    if (typeof sourceModel.anchorConfig.top === 'number') {
                        sourceInputLength = sourceModel.anchorConfig.top;
                    } else if (Array.isArray(sourceModel.anchorConfig.top)) {
                        sourceInputLength = sourceModel.anchorConfig.top.length;
                    }
                }
            }
            if (sourceAnchorIndex < sourceInputLength) {
                return false;
            }

            //业务连接逻辑处理
            let linkAble = true;
            let linkAnchorIndex = [];
            const targetBusData = targetModel.busData;
            if (!targetBusData) {
                linkAble = false;
            } else {
                if (Array.isArray(sourceModel.anchorConfig?.bottom)) {
                    let sourceInputLength = 0;
                    if (sourceModel.anchorConfig.top) {
                        if (typeof sourceModel.anchorConfig.top === 'number') {
                            sourceInputLength = sourceModel.anchorConfig.top;
                        } else if (Array.isArray(sourceModel.anchorConfig.top)) {
                            sourceInputLength = sourceModel.anchorConfig.top.length;
                        }
                    }
                    const outputIndex = sourceAnchorIndex - sourceInputLength;
                    if (sourceModel.anchorConfig.bottom[outputIndex]) {
                        const linkTo = sourceModel.anchorConfig.bottom[outputIndex].linkTo;
                        if (Array.isArray(linkTo)) {
                            const singleLinkTo = linkTo.find((targetConfig) => {
                                if (targetBusData.type === 'export') {
                                    return targetConfig.type == targetBusData.type;
                                } else {
                                    if (targetConfig.value) {
                                        return targetConfig.value == targetBusData.name;
                                    } else if (targetConfig.subType) {
                                        return targetConfig.subType == targetBusData.subType;
                                    } else if (targetConfig.type) {
                                        return targetConfig.type == targetBusData.type;
                                    }
                                    return false;
                                }
                            });
                            linkAble = !!singleLinkTo;
                            linkAnchorIndex = singleLinkTo?.anchorIndex;
                        }
                    }
                } else {
                    linkAble = false;
                }
            }

            if (!linkAble) {
                return false;
            }

            let targetInputLength = 0,
                targetOutputLength = 0;
            //输出锚点只能连接到输入锚点
            if (targetModel.anchorConfig) {
                if (targetModel.anchorConfig.top) {
                    if (typeof targetModel.anchorConfig.top === 'number') {
                        targetInputLength = targetModel.anchorConfig.top;
                    } else if (Array.isArray(targetModel.anchorConfig.top)) {
                        targetInputLength = targetModel.anchorConfig.top.length;
                    }
                }
                if (targetModel.anchorConfig.bottom) {
                    if (typeof targetModel.anchorConfig.bottom === 'number') {
                        targetOutputLength = targetModel.anchorConfig.bottom;
                    } else if (Array.isArray(targetModel.anchorConfig.bottom)) {
                        targetOutputLength = targetModel.anchorConfig.bottom.length;
                    }
                }
            }

            let excludeAnchorIndexs = [];
            //如果节点的输入锚点已经有边接入，则不能继续接入边
            targetNode.getInEdges().forEach((edge) => {
                excludeAnchorIndexs.push(edge.get('targetAnchorIndex'));
            });
            new Array(targetOutputLength).fill(0).map((_, index) => {
                excludeAnchorIndexs.push(targetInputLength + index);
            });
            //排除指定连接锚点之外的输入锚点
            if (Array.isArray(linkAnchorIndex) && linkAnchorIndex.length) {
                new Array(targetInputLength).fill(0).forEach((_, i) => {
                    if (!linkAnchorIndex.includes(i) && !excludeAnchorIndexs.includes(i)) {
                        excludeAnchorIndexs.push(i);
                    }
                });
            }
            return excludeAnchorIndexs;
        },
    },
};
</script>
<style lang="scss" scoped>
.ai-main {
    position: relative;
    overflow: hidden;
    user-select: none;

    .ai-container {
        width: 100%;
        height: 100%;
        border: 1px solid #ccc;
        position: relative;
        .g6-grid-container {
            z-index: 2;
        }
    }

    ::v-deep.context-menu {
        padding: 5px 0;
        position: absolute;
        top: 0;
        left: 0;
        opacity: 1;
        z-index: 10;
        border-radius: 4px;
        -webkit-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
        background-clip: padding-box;
        text-align: left;
        &.ant-menu-vertical {
            .ant-menu-item {
                padding: 0 12px;
            }

            > li.ant-menu-item,
            > li.ant-menu-submenu {
                height: 28px;
                line-height: 28px;
                font-size: 12px;
                color: #333333;
                margin-top: 0;
                margin-bottom: 0 !important;

                &:first-of-type {
                    margin-top: 1px;
                }
                &:last-of-type {
                    margin-bottom: 1px !important;
                }

                &:hover {
                    background-color: #e6f7ff;
                }

                .svg-icon {
                    font-size: 15px;
                    vertical-align: -0.25em;
                }

                .ant-menu-submenu-title {
                    height: 28px;
                    line-height: 28px;
                    font-size: 12px;
                    color: #333333;
                    margin-top: 0;
                    margin-bottom: 0 !important;
                    padding: 0 12px;

                    .svg-icon {
                        font-size: 15px;
                        vertical-align: -0.25em;
                    }
                }
            }
            > li.ant-menu-item-selected {
                background-color: #ffffff;
            }
        }
    }
    ::v-deep.g6-tooltip {
        pointer-events: none;
        &.arrow-top {
            margin-top: 8px;
            &:after {
                content: '';
                position: absolute;
                border: 6px solid transparent;
                border-bottom-color: rgb(33, 35, 43);
                top: 0;
                margin-top: -12px;
                left: 10px;
                pointer-events: none;
            }
        }
        &.arrow-bottom {
            margin-top: -8px;
            &:after {
                content: '';
                position: absolute;
                border: 6px solid transparent;
                border-top-color: rgb(33, 35, 43);
                bottom: 0;
                margin-bottom: -12px;
                left: 10px;
                pointer-events: none;
            }
        }
    }
    ::v-deep.g6-node-tooltip {
        border: 1px solid #17181d;
        border-radius: 4px;
        font-size: 12px;
        color: #ffffff;
        background-color: rgb(33, 35, 43);
        padding: 10px 8px;
        box-shadow: rgb(14, 14, 17) 0px 0px 6px;
        position: relative;
        min-width: 180px;
        max-width: 250px;
        table {
            tr {
                td {
                    word-break: break-all;
                    line-height: 1.8;

                    &:first-of-type {
                        white-space: nowrap;
                        vertical-align: top;
                    }
                }
            }
        }
    }
}
</style>
